#ifndef GAME_ASSET_H
#define GAME_ASSET_H
#include "game_platform.h"
#include "game_file_format.h"

struct loaded_sound {
    uint32 SampleCount;
    uint32 ChannelCount;
    int16 *Samples[2];
};

// 定义一个结构体 `loaded_bitmap`，用于存储加载的位图信息
struct loaded_bitmap {
    v2 AlignPrecentage;
    real32 WidthOverHeight;
    real32 NativeHeight;

    int32 Width;   // 图像的宽度（像素数）
    int32 Height;  // 图像的高度（像素数）
    // Pitch: 图像每行的字节数。它表示从一行的起始位置到下一行起始位置的字节偏移量。
    // 例如，如果图像宽度为100像素，每个像素占用4字节，则一行的 Pitch 值为 100 * 4 = 400字节。
    // Pitch 用于处理非标准图像格式，避免直接依赖图像的宽度来计算每行的内存偏移。
    // 在涉及图像操作时，Pitch 是必须的，尤其是在图像行可能包含填充或字节对齐的情况下。
    int32 Pitch;
    void *Memory;  // 指向图像像素数据的指针，存储像素的颜色值
    void *TextureHandle;
};

struct asset_bitmap_info {
    v2 AlignPercentage;
    const char *FileName;
};

struct asset_sound_info {
    const char *FileName;
    uint32 FirstSampleIndex;
    uint32 SampleCount;
    sound_id NextIDToPlay;
};

struct asset_type {
    uint32 FirstAssetIndex;
    uint32 OnePastLastAssetIndex;
    assets_type_id TypeID;
};

enum asset_state {
    AssetState_Unloaded,
    AssetState_Queued,
    AssetState_Loaded,
};

struct asset_tag {
    uint32 ID;
    real32 Value;
};

struct asset_vector {
    real32 E[Tag_Count];
};

struct asset_group {
    uint32 FirstTagIndex;
    uint32 OnePastLastTagIndex;
};

enum asset_memory_type {
    AssetState_Sound,
    AssetState_Bitmap,
};

struct loaded_font {
    hha_font_glyph *Glyphs;
    real32 *HorizontalAdvance;
    uint32 BitmapIDOffset;
    uint16 *UnicodeMap;
};

enum asset_header_type {
    AssetType_None,
    AssetType_Bitmap,
    AssetType_Sound,
    AssetType_Font,
};

struct asset_memory_header {
    asset_memory_header *Next;
    asset_memory_header *Prev;
    uint32 AssetIndex;
    uint32 TotalSize;
    uint32 GenerationID;
    asset_header_type AssetType;
    union {
        loaded_bitmap Bitmap;
        loaded_sound Sound;
        loaded_font Font;
    };
};

struct asset {
    hha_asset HHA;
    uint32 FileIndex;
    asset_memory_header *Header;

    uint32 State;
};

struct asset_file {
    platform_file_handle Handle;
    hha_header Header;
    hha_asset_type *AssetTypeArray;
    uint32 TagBase;
    uint32 FontBitmapIDOffset;
};

enum asset_memory_block_flag {
    AssetMemory_Used = 0x1,
};
struct asset_memory_block {
    asset_memory_block *Prev;
    asset_memory_block *Next;
    uint64 Flags;
    memory_index Size;
};

#define VERY_LARGE_NUMBER 4096

struct game_assets {
    platform_texture_op_queue *TextureOpQueue;
    uint32 NextGanerationID;
    asset_memory_block MemorySentinel;
    asset_memory_header LoadedAssetSentinel;

    struct transient_state *TranState;
    real32 TagRange[Tag_Count];
    asset *Assets;
    uint32 AssetCount;
    hha_tag *Tags;
    uint32 TagCount;
    asset_type AssetTypes[Asset_Count];

    uint32 FileCount;
    asset_file *Files;

    uint32 OperationLock;

    uint32 InFlightGenerationCount;
    uint32 InFlightGenerations[16];
#if 0
    uint8 *HHAContents;
    asset_type *DEBUGAssetType;
    asset *DEBUGAsset;
    uint32 DEBUGUsedAssetCount;
    uint32 DEBUGUsedTagCount;
#endif
};

inline void BeginAssetLock(game_assets *Assets) {
    for (;;) {
        if (AtomicCompareExchangeUInt32(&Assets->OperationLock, 1, 0) == 0) {
            break;
        }
    }
}

inline void EndAssetLock(game_assets *Assets) {
    CompletePreviousWriteBeforeFutureWrites;
    Assets->OperationLock = 0;
}

inline uint32 BeginGeneration(game_assets *Assets) {
    BeginAssetLock(Assets);
    Assert(Assets->InFlightGenerationCount < ArrayCount(Assets->InFlightGenerations));
    uint32 Result = Assets->NextGanerationID++;
    Assets->InFlightGenerations[Assets->InFlightGenerationCount++] = Result;
    EndAssetLock(Assets);

    return Result;
}

inline void EndGeneration(game_assets *Assets, uint32 GenerationID) {
    BeginAssetLock(Assets);
    for (uint32 Index = 0; Index < Assets->InFlightGenerationCount; ++Index) {
        if (Assets->InFlightGenerations[Index] == GenerationID) {
            Assets->InFlightGenerations[Index] =
                Assets->InFlightGenerations[--Assets->InFlightGenerationCount];
            break;
        }
    }
    EndAssetLock(Assets);
}

internal void RemoveAssetHeaderFromList(asset_memory_header *Header) {
    Header->Prev->Next = Header->Next;
    Header->Next->Prev = Header->Prev;
    Header->Next = Header->Prev = 0;
}

inline void InsertAssetHeaderAtFront(game_assets *Assets, asset_memory_header *Header) {
    asset_memory_header *Sentinel = &Assets->LoadedAssetSentinel;

    Header->Prev = Sentinel;
    Header->Next = Sentinel->Next;

    Header->Next->Prev = Header;
    Header->Prev->Next = Header;
}

inline asset_memory_header *GetAsset(game_assets *Assets, uint32 ID, uint32 GenerationID) {
    BeginAssetLock(Assets);
    Assert(ID <= Assets->AssetCount);
    asset *Asset = Assets->Assets + ID;

    asset_memory_header *Result = 0;
    if (Asset->State == AssetState_Loaded) {
        Result = Asset->Header;
        RemoveAssetHeaderFromList(Result);
        InsertAssetHeaderAtFront(Assets, Result);

        if (Asset->Header->GenerationID < GenerationID) {
            Asset->Header->GenerationID = GenerationID;
        }
        CompletePreviousWriteBeforeFutureWrites;
    }
    EndAssetLock(Assets);

    return Result;
}

inline loaded_bitmap *GetBitmap(game_assets *Assets, bitmap_id ID, uint32 GenerationID) {
    asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID);
    loaded_bitmap *Result = Header ? &Header->Bitmap : 0;
    return Result;
}
inline loaded_sound *GetSound(game_assets *Assets, sound_id ID, uint32 GenerationID) {
    asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID);
    loaded_sound *Result = Header ? &Header->Sound : 0;
    return Result;
}
inline loaded_font *GetFont(game_assets *Assets, font_id ID, uint32 GenerationID) {
    asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID);
    loaded_font *Result = Header ? &Header->Font : 0;
    return Result;
}
inline hha_font *GetFontInfo(game_assets *Assets, font_id ID) {
    Assert(ID.Value <= Assets->AssetCount);
    hha_font *Result = &Assets->Assets[ID.Value].HHA.Font;
    return Result;
}
inline hha_sound *GetSoundInfo(game_assets *Assets, sound_id ID) {
    Assert(ID.Value <= Assets->AssetCount);
    hha_sound *Result = &Assets->Assets[ID.Value].HHA.Sound;
    return Result;
}

inline hha_bitmap *GetBitmapInfo(game_assets *Assets, bitmap_id ID) {
    Assert(ID.Value <= Assets->AssetCount);
    hha_bitmap *Result = &Assets->Assets[ID.Value].HHA.Bitmap;
    return Result;
}

inline bool32 IsValid(bitmap_id ID) {
    bool32 Result = (ID.Value != 0);
    return Result;
}

inline bool32 IsValid(sound_id ID) {
    bool32 Result = (ID.Value != 0);
    return Result;
}

struct hero_bitmaps_id {
    bitmap_id Head;
    bitmap_id Cape;
    bitmap_id Torso;
};

inline sound_id GetNextSoundInChain(game_assets *Assets, sound_id ID) {
    sound_id Result = {};
    hha_sound *Info = GetSoundInfo(Assets, ID);
    switch (Info->Chain) {
        case HHASoundChain_None: {
        } break;
        case HHASoundChain_Loop: {
            Result = ID;
        } break;
        case HHASoundChain_Advance: {
            Result.Value = ID.Value + 1;
        } break;
        default: {
            InvalidCodePath;
        }
    }
    return Result;
}

void EvictAssetsAsNecessary(game_assets *Assets);
void LoadFont(game_assets *Assets, font_id ID, bool32 Immediate);
bitmap_id GetBestMatchBitmapFrom(game_assets *Assets, assets_type_id TypeID,
                                 asset_vector *MatchVector, asset_vector *WeightVector);
void PrefetchBitmap(game_assets *Assets, bitmap_id ID);
real32 GetHorizontalAdvanceForPair(hha_font *Info, loaded_font *Font, uint32 DesiredPrevCodePoint,
                                   uint32 DesiredCodePoint);
bitmap_id GetBitmapForGlyph(game_assets *Assets, hha_font *Info, loaded_font *Font,
                            uint32 DesiredCodePoint);
real32 GetStartingBaselineY(hha_font *Info);
real32 GetLineAdvanceFor(hha_font *Info);
void PrefetchSound(game_assets *Assets, sound_id ID);
bitmap_id GetFirstBitmapFrom(game_assets *Assets, assets_type_id TypeId);
#endif